use chrono::Local;
use sqlite_wasm_rs::sqlite3_stmt;
use crate::db::dao::base_dao::{option_string_to_set};
use crate::db::db::{Db, DbParamType};
use crate::db::pojo::pojo::{ImDepartment};
use crate::db::util::{db_get_int, db_get_int64, db_get_string};
use crate::util::time::{parse_to_time};


pub fn department_inserts(db: Db, departments: &mut Vec<&mut ImDepartment>) {
    if departments.is_empty() {
        return;
    }
    log::info!("department inserts: {:?}", departments);
    let sql = c"insert into im_department(currentUserId, name, code, parentCode, description, status, managerUserId,
        otherManagerUserIds, ext1, ext2, createTime, updateTime) values(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
    let mut params = vec![];

    for department in departments.iter() {
        let status  = department.status.clone();

        let mut param = Vec::new();
        param.push(DbParamType::Str(&*department.current_user_id));
        param.push(DbParamType::Str(&*department.name));
        param.push(DbParamType::Str(&*department.code));
        param.push(DbParamType::OptionStr(department.parent_code.as_ref()));
        param.push(DbParamType::OptionStr(department.description.as_ref()));
        param.push(DbParamType::Int(status as i64));
        param.push(DbParamType::OptionStr(department.manager_user_id.as_ref()));
        param.push(DbParamType::OptionStr(department.other_manager_user_ids.as_ref()));
        param.push(DbParamType::OptionStr(department.ext1.as_ref()));
        param.push(DbParamType::OptionStr(department.ext2.as_ref()));
        param.push(DbParamType::DateTime2(&department.create_date_time));
        param.push(DbParamType::DateTime2(&department.update_date_time));
        params.push(param);
    }
    log::info!("db inserts params: {:?}", params);
    let ids = db.inserts("department insert", sql, params);
    assert_eq!(ids.len(), departments.len(), "批量保存结构，主键异常");
    for (idx, id) in ids.iter().enumerate() {
        let department = &mut departments[idx];
        department.id = Some(id.clone());
    }
}

pub fn department_updates(db: Db, departments: &mut Vec<&mut ImDepartment>) {
    if departments.is_empty() {
        return;
    }
    log::info!("department update: {:?}", departments);

    let sql = "update im_department set name = ?, parentCode = ?, description = ?, status = ?, managerUserId = ?, \
        otherManagerUserIds = ?, ext1 = ?, ext2 = ?, updateTime = ? where code = ?";
    let mut params = vec![];
    for department in departments {
        let mut param = Vec::new();
    
        let status = department.status.clone();
        param.push(DbParamType::Str(&*department.name));
        param.push(DbParamType::OptionStr(department.parent_code.as_ref()));
        param.push(DbParamType::OptionStr(department.description.as_ref()));
        param.push(DbParamType::Int(status as i64));
        param.push(DbParamType::OptionStr(department.manager_user_id.as_ref()));
        param.push(DbParamType::OptionStr(department.other_manager_user_ids.as_ref()));
        param.push(DbParamType::OptionStr(department.ext1.as_ref()));
        param.push(DbParamType::OptionStr(department.ext2.as_ref()));
        param.push(DbParamType::DateTime(Local::now()));
        // param.push(DbParamType::Str(&*department.current_user_id));
        param.push(DbParamType::Str(&*department.code));
        params.push(param);
    }
    log::info!("department update params: {:?}", params);
    db.batch_exec("department update", sql.to_string(), params);
}

pub fn query_departments_by_codes(db: Db, codes: Vec<String>) -> Vec<ImDepartment> {
    if codes.is_empty() { return Vec::new(); }
    let sql_where = codes.iter().map(|code| "?").collect::<Vec<&str>>().join(",");
    let sql = format!("SELECT id, currentUserId, name, code, parentCode, description, status, managerUserId,
        otherManagerUserIds, ext1, ext2, createTime, updateTime FROM im_department where code in ({})", sql_where);
    let mut params = vec![];
    codes.into_iter().for_each(|code| {
        params.push(DbParamType::String(code));
    });
    db.query2("department query", sql.as_str(), params, Box::new(department_wrap))
}

pub fn count_total_department(db: Db)-> usize {
    let size = db.count("department total", c"select count(*) from im_department", Vec::new());
    log::info!("db count: {}", size);
    size
}

pub fn query_departments_for_db(db: Db) -> Vec<ImDepartment> {
    let sql = c"SELECT id, currentUserId, name, code, parentCode, description, status, managerUserId,
        otherManagerUserIds, ext1, ext2, createTime, updateTime FROM im_department";
    let params = vec![];
    db.query("department query", sql, params, Box::new(department_wrap))
}

fn department_wrap(stmt: *mut sqlite3_stmt)-> ImDepartment {
    unsafe  {
        let id = db_get_int64(stmt, 0);
        let current_user_id = db_get_string(stmt, 1).unwrap();
        let name = db_get_string(stmt, 2).unwrap();
        let code = db_get_string(stmt, 3).unwrap();
        let parent_code = db_get_string(stmt, 4);
        let description = db_get_string(stmt, 5);
        let status = db_get_int(stmt, 6);
        let manager_user_id = db_get_string(stmt, 7);
        let other_manager_user_ids = db_get_string(stmt, 8);
        let other_manager_user_id = option_string_to_set(&other_manager_user_ids);
        let ext1 = db_get_string(stmt, 9);
        let ext2 = db_get_string(stmt, 10);
        let create_time_str = db_get_string(stmt, 11).unwrap();
        let update_time_str = db_get_string(stmt, 12).unwrap();

        let status2 = serde_json::from_str(status.to_string().as_str()).unwrap();
        ImDepartment {
            id: Some(id),
            current_user_id,
            code,
            name,
            description,
            parent_code,
            manager_user_id,
            other_manager_user_id,
            other_manager_user_ids,
            status: status2,
            create_date_time: parse_to_time(create_time_str),
            update_date_time: parse_to_time(update_time_str),
            ext1,
            ext2,
        }
    }
}